事务保证一组数据库操作,要么全部成功,要么全部失败。
ACID
- Atomicity(原子性)
- Consistency(一致性)
- Isolation(隔离性)
- Durability(持久性)
隔离性
为什么需要隔离性
因为多个事务同时执行的时候,容易引发脏读、不可重复读、幻读等问题。
- 脏读
一个事务在处理过程中,读取了别的事务未提交的脏数据。如果别的事务回滚,那么我们拿到的数据是不准确的,由此会造成一定的问题。
不可重复读
一个事务在处理过程,两次查询的数据不一样。这是因为第二次查询读取了别的事务已经提交的变更。那么我们在这个事务操作中就能看到别的事务提交的变更。幻读
一个事务在处理过程中,两次查询的数据不一样,第二次查询读取了别的事务已经提交的变更。
- 不可重复读和幻读都在还未提交的事务过程中读取了另一个已经提交的事务变更。不可重读读针对的是同一个数据项,而幻读针对的是一批数据整体。例如小编去取钱,
- 不可重复读:重点在于修改,两次读取的数据不一样
- 幻读:重点在新增和删除,比如这里有一个事务1要查询公司里面工资1000元以上的职工有10人,这时另一个事务2里面,HR新增了一个新员工,工资也在1000元以上。这时候事务1在次查询,发现工资1000元以上的人员增加到了11个人。
隔离级别
多个事务同时执行,引发的脏读、不可重复读、幻读等问题,就产生了‘隔离级别’的概念。隔离越严实,效率就越低,但数据一致性的程度越高。由此需要在隔离性和效率之间寻找一个平衡点。
- 读未提交(read uncommitted)
一个事务还未提交的时候,它的变更能被别的事务看到。
- 读提交(read committed)
一个事务提交之后,它的改动才能被其他事务看到。事务A修改了一项数据,B事务读取不到变动,只有等事务A提交之后,B事务才能够读取。由此可以避免脏读,但是会造成不可重复读和幻读。
- 可重复读(repeatable read)
一个事务在执行过程中看到的数据和事务启动是看到的数据时一致的。当事务A开始读取一条记录的时候,事务B是不能够进行修改的。由此可以避免不可重复读,但是还是会造成幻读(事务A读取一些数据,事务B插入或者删除一些数据,这时候在此读取,会发现数据多了或者少了)。
- 串行化(serializable)
‘读’和‘写’会进行加锁,读写锁发生冲突时候,后访问的事务必须等前一个事务执行完成。
视图
事务的隔离级别往往和视图有关系,不同的隔离级别创建视图的顺序是不一样。视图可以理解为数据副本,每次创建视图,将当前已持久化的数据创建副本,后面直接从副本读取,从而达到数据隔离。
- 读未提交
没有视图概念,不同事务在读未提交的隔离级别上直接读取记录的最新值,容易出现脏读的情况。
- 读提交
视图在每个SQL开始执行的时候创建的。所以第二次查询的时候,别的事务已经提交了数据,重新建立的视图里面包含已经变更的数据。因此能够读取别的事务修改的数据。
- 可重复读
视图是在事务启动的时候创建的。(TODO)
- 串行化
直接用加锁的方式避免并行访问
事务隔离的实现
MVCC(多版本并发控制)
同一条记录在系统中可以存在多个版本。
避免使用长事务
- 长事务存在很老的事务视图,在未提交之前,所有的回滚记录都必须保留,会导致大量占用存储空间。
- 占用锁资源,拖垮数据库。